<?php
defined('SYS_PATH') or die('No direct access allowed.');

/**
 * Captcha driver class.
 *
 * $Id: driver.php 338 2009-10-16 02:37:46Z wangzh $
 *
 * @package    Captcha
 * @author     Ko Team, Eric
 * @copyright  (c) 2007-2008 Ko Team
 * @license    http://kophp.com/license.html
 */
abstract class Captcha_Driver
{

    // The correct Captcha challenge answer
    protected $response;

    // Image resource identifier and type ("png", "gif" or "jpeg")
    protected $image;

    protected $image_type = 'png';

    /**
     * Constructs a new challenge.
     *
     * @return  void
     */
    public function __construct ()
    {
        // nothing to do.
    }
    
    /**
     * Init a new challenge.
     *
     * @return  void
     */
    public function initChallenge ()
    {
        // Generate a new challenge
        $this->response = $this->generateChallenge();
        // Store the correct Captcha response in a session
        Session::instance()->set('captcha_response', sha1(strtoupper($this->response)));
    }    
    /**
     * Generate a new Captcha challenge.
     *
     * @return  string  the challenge answer
     */
    abstract public function generateChallenge ();

    /**
     * Output the Captcha challenge.
     *
     * @return  mixed    the rendered Captcha (e.g. an image, riddle, etc.)
     */
    abstract public function render ();

    /**
     * Validates a Captcha response from a user.
     *
     * @param   string   captcha response
     * @return  boolean
     */
    public function valid ($response)
    {
        return (sha1(strtoupper($response)) === Session::instance()->get('captcha_response'));
    }

    /**
     * Returns the image type.
     *
     * @param   string        filename
     * @return  string|FALSE  image type ("png", "gif" or "jpeg")
     */
    public function getImageType ($filename)
    {
        switch (strtolower(substr(strrchr($filename, '.'), 1))) {
            case 'png':
                return 'png';
            case 'gif':
                return 'gif';
            case 'jpg':
            case 'jpeg':
                // Return "jpeg" and not "jpg" because of the GD2 function names
                return 'jpeg';
            default:
                return FALSE;
        }
    }

    /**
     * Creates an image resource with the dimensions specified in config.
     * If a background image is supplied, the image dimensions are used.
     *
     * @throws  KoException  if no GD2 support
     * @param   string  path to the background image file
     * @return  void
     */
    public function createImage ($background = NULL)
    {
        // Check for GD2 support
        if (! function_exists('imagegd2'))
            throw new KoException('captcha.requires_GD2');
            // Create a new image (black)
        $this->image = imagecreatetruecolor(Captcha::$config['width'], Captcha::$config['height']);
        // Use a background image
        if (! empty($background)) {
            // Create the image using the right function for the filetype
            $function = 'imagecreatefrom' . $this->getImageType($background);
            $this->background_image = $function($background);
            // Resize the image if needed
            if (imagesx($this->background_image) !== Captcha::$config['width'] or imagesy($this->background_image) !== Captcha::$config['height']) {
                imagecopyresampled($this->image, $this->background_image, 0, 0, 0, 0, Captcha::$config['width'], Captcha::$config['height'], imagesx($this->background_image), imagesy($this->background_image));
            }
            // Free up resources
            imagedestroy($this->background_image);
        }
    }

    /**
     * Fills the background with a gradient.
     *
     * @param   resource  gd image color identifier for start color
     * @param   resource  gd image color identifier for end color
     * @param   string    direction: 'horizontal' or 'vertical', 'random' by default
     * @return  void
     */
    public function gradientImage ($color1, $color2, $direction = NULL)
    {
        $directions = array('horizontal' , 'vertical');
        // Pick a random direction if needed
        if (! in_array($direction, $directions)) {
            $direction = $directions[array_rand($directions)];
            // Switch colors
            if (mt_rand(0, 1) === 1) {
                $temp = $color1;
                $color1 = $color2;
                $color2 = $temp;
            }
        }
        // Extract RGB values
        $color1 = imagecolorsforindex($this->image, $color1);
        $color2 = imagecolorsforindex($this->image, $color2);
        // Preparations for the gradient loop
        $steps = ($direction === 'horizontal') ? Captcha::$config['width'] : Captcha::$config['height'];
        $r1 = ($color1['red'] - $color2['red']) / $steps;
        $g1 = ($color1['green'] - $color2['green']) / $steps;
        $b1 = ($color1['blue'] - $color2['blue']) / $steps;
        if ($direction === 'horizontal') {
            $x1 = & $i;
            $y1 = 0;
            $x2 = & $i;
            $y2 = Captcha::$config['height'];
        } else {
            $x1 = 0;
            $y1 = & $i;
            $x2 = Captcha::$config['width'];
            $y2 = & $i;
        }
        // Execute the gradient loop
        for ($i = 0; $i <= $steps; $i ++) {
            $r2 = $color1['red'] - floor($i * $r1);
            $g2 = $color1['green'] - floor($i * $g1);
            $b2 = $color1['blue'] - floor($i * $b1);
            $color = imagecolorallocate($this->image, $r2, $g2, $b2);
            imageline($this->image, $x1, $y1, $x2, $y2, $color);
        }
    }

    /**
     * Returns the img html element or outputs the image to the browser.
     *
     * @param   boolean  html output
     * @return  mixed    html string or void
     */
    public function renderImage ()
    {
        // Send the correct HTTP header
        header('Content-Type: image/' . $this->image_type);
        header("Cache-Control: no-cache, must-revalidate");
        header("Pragma: no-cache");
        // Pick the correct output function
        $function = 'image' . $this->image_type;
        $function($this->image);
        // Free up resources
        imagedestroy($this->image);
    }
} // End Captcha Driver